home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 2002 #3 / Amiga Plus CD - 2002 - No. 03.iso / AmiSoft / Util / Arc / Checkx.lha / CheckX / sources / SortCheckX.c < prev    next >
C/C++ Source or Header  |  2002-12-30  |  21KB  |  613 lines

  1. #define NAME         "SortCheckX"
  2. #define REVISION     "3"
  3. #define DISTRIBUTION "(Freeware) "
  4. #define DATE         "17.08.2002"
  5. #define VERSION      "1"
  6. #define AUTHOR       "by Dirk Stöcker <stoecker@epost.de>"
  7.  
  8. /* Programmheader
  9.  
  10.         Name:           SortCheckX
  11.         Author:         SDI
  12.         Distribution:   PD
  13.         Description:    sorts CheckX output by file name to make comparisons possibly
  14.         Compileropts:   -
  15.         Linkeropts:     -gsi
  16.  
  17.  1.0   14.08.02 : first version
  18.  1.1   15.08.02 : added OLDFIX, CRC and OUTFILE
  19.  1.2   16.08.02 : added buffered Output, optimized
  20.  1.3   17.08.02 : added secondary filesystem support
  21. */
  22.  
  23. #include <proto/dos.h>
  24. #include <proto/exec.h>
  25. #include <exec/memory.h>
  26.  
  27. #define version "$VER: " NAME " " VERSION "." REVISION " (" DATE ") " DISTRIBUTION AUTHOR
  28. #define PARAM           "INFILE/A,OUTFILE,OLDFIX/S,CRC/S"
  29.  
  30. struct Args {
  31.   STRPTR infile;
  32.   STRPTR outfile;
  33.   ULONG  oldfix; /* tries to reduce the differences by fixing older texts */
  34.   ULONG  crc;
  35. };
  36.  
  37. struct CNode {
  38.   struct CNode *Prev;
  39.   struct CNode *Next;
  40.   struct CNode *Sub;
  41.   struct CNode *Head;
  42.   ULONG         BufferPos; /* invalid after Sorting head structure */
  43.   ULONG         Size;
  44.   UWORD         Flags;
  45. };
  46.  
  47. #define FLAG_DISKIMAGE  (1<<0)
  48. #define FLAG_INFOTEXT   (1<<1)
  49. #define FLAG_UNUSED     (1<<2)
  50. #define FLAG_CRC        (1<<3)
  51. #define FLAG_FILESYSTEM (1<<4)
  52.  
  53. #define OUTBUFSIZE 16384
  54.  
  55. struct MyOutBuf {
  56.   ULONG              CurSize;
  57.   struct ExecBase   *SysBase;
  58.   struct DosLibrary *DOSBase;
  59.   BPTR               FileHandle;
  60.   UBYTE              Buffer[OUTBUFSIZE];
  61. };
  62.  
  63. struct MyReplace {
  64.   UWORD Offset;
  65.   UBYTE OldSize;
  66.   UBYTE NewSize;
  67. };
  68.  
  69. /* This is a string field of replace strings:
  70. First comes old, second the new string. Each ends with a '\n'.
  71. The ReplaceField[] structure below contains offset and element sizes of this
  72. string field.
  73.  
  74. Why? Because as result SortCheckX has no Relocs and is very short ;-)
  75. */
  76.  
  77. static const UBYTE StringField[] = {
  78. "CheckX-Error 12: file is empty\n"
  79. "CheckX-Warning 4: file is empty\n"
  80.  
  81. "CheckX-Error 4: read or write failed\n"
  82. "CheckX-Error 4: reading failed\n"
  83.  
  84. "CheckX-Error 100: not enough memory\n"
  85. "CheckX-Error 12: not enough memory for full tests\n"
  86.  
  87. "DMS-Archive\n"
  88. "DMS (ARCHIVE)\n"
  89.  
  90. "LhA-Archive\n"
  91. "LhA (ARCHIVE)\n"
  92.  
  93. "LhA-SFX-Archive\n"
  94. "LhA SFX (ARCHIVE)\n"
  95.  
  96. "LhA-SFX (ARCHIVE)\n"
  97. "LhA SFX (ARCHIVE)\n"
  98.  
  99. "LzX-Archive\n"
  100. "LZX (ARCHIVE)\n"
  101.  
  102. "Zip-Archive\n"
  103. "Zip (ARCHIVE)\n"
  104.  
  105. "ZOO-Archive\n"
  106. "Zoo (ARCHIVE)\n"
  107.  
  108. "ZOO (ARCHIVE)\n"
  109. "Zoo (ARCHIVE)\n"
  110. };
  111.  
  112. #ifdef MAKEREPLACEFIELD /* in caseone is to lazy to count by hand ;-) */
  113. void main(void)
  114. {
  115.   LONG i = 0, n = 0, last = -1;
  116.  
  117.   do
  118.   {
  119.     if(StringField[i] == '\n')
  120.     {
  121.       if(n++ & 1)
  122.         Printf(/*{*/" %2ld},\n", i-last);
  123.       else
  124.         Printf("{ %3ld, %2ld,"/*}*/, last+1, i-last);
  125.       last = i;
  126.     }
  127.   } while(StringField[++i]);
  128.   Printf("{   0,  0,  0}\n");
  129. }
  130. #else
  131. static const struct MyReplace ReplaceField[] = {
  132. {   0, 31, 32},
  133. {  63, 37, 31},
  134. { 131, 36, 50},
  135. { 217, 12, 14},
  136. { 243, 12, 14},
  137. { 269, 16, 18},
  138. { 303, 18, 18},
  139. { 339, 12, 14},
  140. { 365, 12, 14},
  141. { 391, 12, 14},
  142. { 417, 14, 14},
  143. {   0,  0,  0}
  144. };
  145.  
  146. static LONG mystrncmp(STRPTR a, STRPTR b, ULONG s);
  147. static void SortSubTree(struct CNode *curnode, STRPTR mem, struct ExecBase *SysBase);
  148. static void MyOutput(STRPTR mem, ULONG size, struct MyOutBuf *outbuf);
  149.  
  150. ULONG start(void)
  151. {
  152.   struct DosLibrary *DOSBase;
  153.   struct ExecBase *SysBase = (*((struct ExecBase **) 4));
  154.  
  155.   { /* test for WB and reply startup-message */
  156.     struct Process *task;
  157.     if(!(task = (struct Process *) FindTask(0))->pr_CLI)
  158.     {
  159.       WaitPort(&task->pr_MsgPort);
  160.       Forbid();
  161.       ReplyMsg(GetMsg(&task->pr_MsgPort));
  162.       return RETURN_FAIL;
  163.     }
  164.   }
  165.  
  166.   if((DOSBase = (struct DosLibrary *) OpenLibrary("dos.library", 37)))
  167.   {
  168.     struct Args args;
  169.     struct RDArgs *rda;
  170.  
  171.     args.oldfix = args.crc = 0;
  172.     args.outfile = 0;
  173.     if((rda = ReadArgs(PARAM, (LONG *) &args, 0)))
  174.     {
  175.       BPTR fh;
  176.       if(args.crc)
  177.         args.crc = FLAG_CRC;
  178.       if((fh = Open(args.infile, MODE_OLDFILE)))
  179.       {
  180.         struct FileInfoBlock *fib;
  181.  
  182.         if((fib = (struct FileInfoBlock *) AllocDosObject(DOS_FIB, 0)))
  183.         {
  184.           if(ExamineFH(fh, fib))
  185.           {
  186.             STRPTR mem = version; /* removed by optimizer */
  187.             if((mem = (STRPTR) AllocMem(fib->fib_Size+1, 0)))
  188.             {
  189.               mem[fib->fib_Size] = '\n';
  190.               if(Read(fh, mem, fib->fib_Size) == fib->fib_Size)
  191.               {
  192.                 /* This does no error checking and will produce total crap
  193.                    with non-CheckX files, but should make no crash or other
  194.                    dangerous error. */
  195.                 ULONG i, depth1 = 0, depth2;
  196.                 /* depth 1 --> number of '*', depth2 --> number of - types */
  197.                 struct CNode *cbuf, *cbufp, *curnode;
  198.  
  199.                 /* depth2 is a short time used to count number of lines */
  200.                 for(i = depth2 = 0; i < fib->fib_Size; ++i)
  201.                 {
  202.                   if(mem[i] == '\n' && mem[i+1] != ' ')
  203.                   ++depth2;
  204.                 }
  205.                 depth2 += 5; /* security */
  206.  
  207.                 if((cbuf = AllocVec(depth2*(sizeof(struct CNode))+sizeof(struct MyOutBuf), MEMF_CLEAR)))
  208.                 {
  209.                   struct MyOutBuf *outbuf = (struct MyOutBuf *) (cbuf+depth2);
  210.  
  211.                   outbuf->FileHandle = Output();
  212.                   outbuf->DOSBase = DOSBase;
  213.                   outbuf->SysBase = SysBase;
  214.                   if(!args.outfile || (outbuf->FileHandle = Open(args.outfile, MODE_NEWFILE)))
  215.                   {
  216.                     depth2 = i = 0;
  217.  
  218.                     while(i < fib->fib_Size) /* skip the start texts */
  219.                     {
  220.                       if(!mystrncmp("Virus-Checking", mem+i, 14))
  221.                       {
  222.                         while(mem[i++] != '\n')
  223.                          ;
  224.                       }
  225.                       else if(!mystrncmp("The xvs.library", mem+i, 15))
  226.                       {
  227.                         while(mem[i++] != '\n')
  228.                           ;
  229.                       }
  230.                       else if(!mystrncmp("Your system memory", mem+i, 18))
  231.                       {
  232.                         while(mem[i++] != '\n')
  233.                           ;
  234.                       }
  235.                       else
  236.                         break;
  237.                     }
  238.  
  239.                     cbufp = cbuf;
  240.  
  241.                     /* the master header, no bytes */
  242.                     curnode = cbufp++;
  243.                     cbufp->Head = curnode;
  244.                     curnode->Sub = cbufp;
  245.  
  246.                     /* the first line */
  247.                     curnode = cbufp++;
  248.                     curnode->BufferPos = i;
  249.                     while(mem[i++] != '\n')
  250.                       ;
  251.  
  252.                     while(i < fib->fib_Size && mem[i] != '\n'
  253.                     && !(SetSignal(0L,0L) & SIGBREAKF_CTRL_C))
  254.                     /* this is either the empty line or file end */
  255.                     {
  256.                       ULONG d, i2 = 0;
  257.  
  258.                       if(mem[i] != ' ') /* if ' ' attach a type line to current type */
  259.                       {
  260.                         if(args.crc)
  261.                         {
  262.                           for(i2 = 0; i2 < 9 && mem[i+i2] != '\n'; ++i2)
  263.                             ;
  264.                           if(i2 != 9) i2 = 0;
  265.                         }
  266.  
  267.                         if(!curnode->Size)
  268.                           curnode->Size = i-curnode->BufferPos;
  269.                         for(d = 0; mem[i+i2+d] == '*'; ++d)
  270.                           ;
  271.                         if(d > depth1) /* a subtype */
  272.                         {
  273.                           cbufp->Head      = curnode;
  274.                           cbufp->BufferPos = i;
  275.                           cbufp->Flags     = args.crc;
  276.                           curnode->Sub     = cbufp;
  277.                           curnode = cbufp++;
  278.                           ++depth1;
  279.                         }
  280.                         else if(d == depth1) /* equal or subtype 2 */
  281.                         {
  282.                           if(!mystrncmp("--infotext", mem+i+i2+d, 10))
  283.                           {
  284.                             if(curnode->Flags & (FLAG_INFOTEXT|FLAG_DISKIMAGE|FLAG_FILESYSTEM))
  285.                             {
  286.                               cbufp->Prev      = curnode;
  287.                               cbufp->Head      = curnode->Head;
  288.                               cbufp->BufferPos = i;
  289.                               cbufp->Flags     = FLAG_INFOTEXT|args.crc;
  290.                               curnode->Next    = cbufp;
  291.                               curnode = cbufp++;
  292.                             }
  293.                             else
  294.                             {
  295.                               cbufp->Head      = curnode;
  296.                               cbufp->BufferPos = i;
  297.                               cbufp->Flags     = FLAG_INFOTEXT|args.crc;
  298.                               if(curnode->Sub)
  299.                               {
  300.                                 for(curnode = curnode->Sub; curnode->Next; curnode = curnode->Next)
  301.                                   ;
  302.                                 curnode->Next = cbufp;
  303.                                 cbufp->Prev = curnode;
  304.                               }
  305.                               else
  306.                                 curnode->Sub = cbufp;
  307.                               curnode = cbufp++;
  308.                               ++depth2;
  309.                             }
  310.                           }
  311.                           else if(!mystrncmp("-disk image", mem+i+i2+d, 11))
  312.                           {
  313.                             if(curnode->Flags & (FLAG_INFOTEXT|FLAG_DISKIMAGE|FLAG_FILESYSTEM))
  314.                             {
  315.                               cbufp->Prev      = curnode;
  316.                               cbufp->Head      = curnode->Head;
  317.                               cbufp->BufferPos = i;
  318.                               cbufp->Flags     = FLAG_DISKIMAGE|args.crc;
  319.                               curnode->Next    = cbufp;
  320.                               curnode = cbufp++;
  321.                             }
  322.                             else
  323.                             {
  324.                               cbufp->Head      = curnode;
  325.                               cbufp->BufferPos = i;
  326.                               cbufp->Flags     = FLAG_DISKIMAGE|args.crc;
  327.                               if(curnode->Sub)
  328.                               {
  329.                                 for(curnode = curnode->Sub; curnode->Next; curnode = curnode->Next)
  330.                                   ;
  331.                                 curnode->Next = cbufp;
  332.                                 cbufp->Prev = curnode;
  333.                               }
  334.                               else
  335.                                 curnode->Sub = cbufp;
  336.                               curnode = cbufp++;
  337.                               ++depth2;
  338.                             }
  339.                           }
  340.                           else /* new equal type */
  341.                           {
  342.                             if(curnode->Flags & (FLAG_INFOTEXT|FLAG_DISKIMAGE|FLAG_FILESYSTEM))
  343.                             {
  344.                               --depth2;
  345.                               curnode = curnode->Head;
  346.                               SortSubTree(curnode, mem, SysBase);
  347.                             }
  348.                             cbufp->Prev      = curnode;
  349.                             cbufp->Head      = curnode->Head;
  350.                             cbufp->BufferPos = i;
  351.                             cbufp->Flags     = args.crc;
  352.                             curnode->Next    = cbufp;
  353.                             curnode = cbufp++;
  354.                           }
  355.                         }
  356.                         else /* leave that level depth1-d times, take care for depth2 and sizes */
  357.                         {
  358.                           /* set sizes! */
  359.                           while(depth1 > d || curnode->Flags & (FLAG_INFOTEXT|FLAG_DISKIMAGE|FLAG_FILESYSTEM))
  360.                           {
  361.                             if(curnode->Flags & (FLAG_INFOTEXT|FLAG_DISKIMAGE|FLAG_FILESYSTEM))
  362.                               --depth2;
  363.                             else
  364.                               --depth1;
  365.                             curnode = curnode->Head;
  366.                             SortSubTree(curnode, mem, SysBase);
  367.                           }
  368.                           continue; /* parse this again */
  369.                         }
  370.                       }
  371.                       else
  372.                       {
  373.                         for(d = 0; mem[i+d] == ' '; ++d)
  374.                           ;
  375.                         if(args.crc) d -= 9;
  376.                         if(d <= depth1 || curnode->Flags & (FLAG_INFOTEXT|FLAG_DISKIMAGE|FLAG_FILESYSTEM))
  377.                         {
  378.                           if(!curnode->Size)
  379.                             curnode->Size = i-curnode->BufferPos;
  380.                           while(depth1 >= d)
  381.                           {
  382.                             if(curnode->Flags & (FLAG_INFOTEXT|FLAG_DISKIMAGE|FLAG_FILESYSTEM))
  383.                               --depth2;
  384.                             else
  385.                               --depth1;
  386.                             curnode = curnode->Head;
  387.                             SortSubTree(curnode, mem, SysBase);
  388.                           }
  389.                           cbufp->Prev      = curnode;
  390.                           cbufp->Head      = curnode->Head;
  391.                           cbufp->BufferPos = i;
  392.                           cbufp->Flags     = FLAG_FILESYSTEM|args.crc;
  393.                           curnode->Next    = cbufp;
  394.                           curnode = cbufp++;
  395.                         }
  396.  
  397.                       }
  398.                       while(mem[i++] != '\n')
  399.                         ;
  400.                     }
  401.                     if(i > fib->fib_Size)
  402.                       i = fib->fib_Size;
  403.                     if(!curnode->Size)
  404.                       curnode->Size = i-curnode->BufferPos;
  405.                     while(curnode->Head)
  406.                     {
  407.                       curnode = curnode->Head;
  408.                       SortSubTree(curnode, mem, SysBase);
  409.                     }
  410.  
  411.                     /* curnode now represents a complete tree, where each entry starts
  412.                        with a filename (or disk image, infotext) and may have additional
  413.                        texts after the first line. For a file information tree only use
  414.                        first line (except for filesystem which has no name)!
  415.                      */
  416.                     if(curnode->BufferPos && !(SetSignal(0L,0L) & SIGBREAKF_CTRL_C))
  417.                       MyOutput(mem, curnode->BufferPos, outbuf);
  418.                     cbufp = cbuf->Sub;
  419.                     while(cbufp && !(SetSignal(0L,0L) & SIGBREAKF_CTRL_C))
  420.                     {
  421.                       if(args.oldfix)
  422.                       {
  423.                         STRPTR buf, bufend, beg;
  424.  
  425.                         buf = mem+cbufp->BufferPos;
  426.                         bufend = buf+cbufp->Size;
  427.                         while(buf < bufend)
  428.                         {
  429.                           beg = buf;
  430.                           if(*buf == ' ')
  431.                           {
  432.                             const struct MyReplace *mr = ReplaceField;
  433.  
  434.                             while(*buf == ' ')
  435.                               ++buf;
  436.                             MyOutput(beg, buf-beg, outbuf);
  437.                             beg = buf;
  438.  
  439.                             while(mr->OldSize && mystrncmp(buf,
  440.                             ((STRPTR)StringField)+mr->Offset, mr->OldSize))
  441.                               ++mr;
  442.                             if(mr->OldSize)
  443.                             {
  444.                               MyOutput(((STRPTR)StringField)+mr->OldSize+mr->Offset,
  445.                               mr->NewSize, outbuf);
  446.                               buf += mr->OldSize;
  447.                               continue;
  448.                             }
  449.                           }
  450.                           while(buf < bufend && *(buf++) != '\n')
  451.                             ;
  452.                           MyOutput(beg, buf-beg, outbuf);
  453.                         }
  454.                       }
  455.                       else
  456.                         MyOutput(mem+cbufp->BufferPos, cbufp->Size, outbuf);
  457.  
  458.                       if(cbufp->Sub) cbufp = cbufp->Sub;
  459.                       else if(cbufp->Next) cbufp = cbufp->Next;
  460.                       else
  461.                       {
  462.                         cbufp = cbufp->Head;
  463.                         while(cbufp && !cbufp->Next)
  464.                           cbufp = cbufp->Head;
  465.                         if(cbufp) cbufp = cbufp->Next;
  466.                       }
  467.                     }
  468.                     if(i < fib->fib_Size && !(SetSignal(0L,0L) & SIGBREAKF_CTRL_C))
  469.                       MyOutput(mem+i, fib->fib_Size-i, outbuf);
  470.                     if(outbuf->CurSize)
  471.                       Write(outbuf->FileHandle, outbuf->Buffer, outbuf->CurSize); /* Flush buffer */
  472.                     if(args.outfile)
  473.                       Close(outbuf->FileHandle);
  474.                   }
  475.                   FreeVec(cbuf);
  476.                 }
  477.               }
  478.               FreeMem(mem, fib->fib_Size+1);
  479.             }
  480.           }
  481.           FreeDosObject(DOS_FIB, fib);
  482.         }
  483.         if(fh)
  484.           Close(fh);
  485.       }
  486.  
  487.       FreeArgs(rda);
  488.     }
  489.  
  490.     CloseLibrary((struct Library *) DOSBase);
  491.   }
  492.   return 0;
  493. }
  494.  
  495. #define DOSBase outbuf->DOSBase
  496. #define SysBase outbuf->SysBase
  497. static void MyOutput(STRPTR mem, ULONG size, struct MyOutBuf *outbuf)
  498. {
  499.   ULONG maxs;
  500.   do
  501.   {
  502.     maxs = OUTBUFSIZE - outbuf->CurSize;
  503.     if(maxs > size)
  504.       maxs = size;
  505.     CopyMem(mem, outbuf->Buffer+outbuf->CurSize, maxs);
  506.     outbuf->CurSize += maxs;
  507.     mem += maxs;
  508.     size -= maxs;
  509.     if(outbuf->CurSize == OUTBUFSIZE)
  510.     {
  511.       Write(outbuf->FileHandle, outbuf->Buffer, OUTBUFSIZE);
  512.       outbuf->CurSize = 0;
  513.     }
  514.   } while(size);
  515. }
  516. #undef SysBase
  517. #undef DOSBase
  518.  
  519. static LONG mystrncmp(STRPTR a, STRPTR b, ULONG s)
  520. {
  521.   while(s && *a && *a == *b)
  522.   {
  523.     ++a; ++b; --s;
  524.   }
  525.   return s ? (*a - *b) : 0;
  526. }
  527.  
  528. #define mytolower(a) ((a >= 'A' && a <= 'Z') || (a >= 0xC0 && a <= 0xDE) ? a+32 : a)
  529.  
  530. /* Sorts case insensitive, when equal the case is used for sorting,
  531.    if still equal the order is undefined! */
  532. static LONG MyNodeComp(struct CNode *list, struct CNode *new, STRPTR mem)
  533. {
  534.   STRPTR l, n;
  535.   LONG d, e;
  536.  
  537.   if(list->Flags & (FLAG_UNUSED|FLAG_DISKIMAGE|FLAG_INFOTEXT|FLAG_FILESYSTEM)) /* always greater */
  538.     return 1000;
  539.  
  540.   l = mem+list->BufferPos;
  541.   n = mem+new->BufferPos;
  542.   if(new->Flags & FLAG_CRC)
  543.   {
  544.     for(d = 0; d < 9 && l[d] != '\n' && n[d] != '\n'; ++d)
  545.       ;
  546.     if(d == 9)
  547.     {
  548.       l += d;
  549.       n += d;
  550.     }
  551.   }
  552.   d = 0;
  553.  
  554.   while(!(e = (mytolower(*l) - mytolower(*n))) && *l != '\n')
  555.   {
  556.     if(*l != *n && !d) /* sort by case if equal */
  557.       d = *l-*n;
  558.     ++l; ++n;
  559.   }
  560.   return (e ? e : d);
  561. }
  562.  
  563. static struct CNode *InsertFront(struct CNode *list, struct CNode *newnode)
  564. {
  565.   struct CNode *next;
  566.  
  567.   if((next = newnode->Next))
  568.     next->Prev = 0;
  569.  
  570.   newnode->Next = list;
  571.   if((newnode->Prev = list->Prev))
  572.     newnode->Prev->Next = newnode;
  573.   list->Prev = newnode;
  574.  
  575.   return next;
  576. }
  577.  
  578. static void SortSubTree(struct CNode *curnode, STRPTR mem, struct ExecBase *SysBase)
  579. {
  580.   struct CNode *oldlist, *list, dummylast;
  581.   /* list = list of new nodes */
  582.   /* oldlist = list of old nodes */
  583.  
  584.   if(!(oldlist = curnode->Sub))
  585.     return;
  586.   dummylast.Prev = dummylast.Next = 0;
  587.   dummylast.Flags = FLAG_UNUSED;
  588.   list = &dummylast; /* the dummy entry is to make work easier and faster */
  589.  
  590.   while((oldlist = InsertFront(list, oldlist)) && !(SetSignal(0L,0L) & SIGBREAKF_CTRL_C))
  591.   {
  592.     if(oldlist->Flags & (FLAG_DISKIMAGE|FLAG_INFOTEXT|FLAG_FILESYSTEM))
  593.       list = &dummylast; /* insert at end - in correct (unsorted) order */
  594.     else if(MyNodeComp(list, oldlist, mem) <= 0)
  595.     {
  596.       list = list->Next;
  597.       while(MyNodeComp(list, oldlist, mem) <= 0 && !(SetSignal(0L,0L) & SIGBREAKF_CTRL_C))
  598.         list = list->Next;
  599.     }
  600.     else /* insert before this */
  601.     {
  602.       while(list->Prev && (MyNodeComp(list->Prev, oldlist, mem) > 0) && !(SetSignal(0L,0L) & SIGBREAKF_CTRL_C))
  603.         list = list->Prev;
  604.     }
  605.   }
  606.   while(list->Prev)
  607.     list = list->Prev;
  608.  
  609.   dummylast.Prev->Next = 0; /* remove last entry */
  610.   curnode->Sub = list; /* the start of list */
  611. }
  612. #endif /* MAKEREPLACEFIELD */
  613.